package com.cmcc.example.face.noSdk.service;

import com.alibaba.fastjson.JSON;
import com.cmcc.example.face.common.util.CheckOutUtil;
import com.cmcc.example.face.noSdk.common.HttpURI;
import com.cmcc.example.face.noSdk.common.vo.request.*;
import com.cmcc.example.face.noSdk.common.vo.response.*;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * <name>FaceService</name>
 * 人脸检测相关处理逻辑
 * </p >
 *
 * @author arabira
 * @version 1.0 2020-04-01
 * @since 1.0
 */
@Slf4j
@Service
public class FaceService {

    @Autowired
    private CommonService commonService;

    /**
     * 人脸比对
     * @param image1
     * @param image2
     * @return
     * @throws IOException
     */
    public EcloudBaseResponseVO<FaceMatcherResponseVO> matcher(MultipartFile image1, MultipartFile image2) throws IOException {
        FaceMatchVO faceMatchVO = new FaceMatchVO(image1, image2);
        String matcherUrl = commonService.compileUrl(HttpURI.FACE_MATCHER);
        log.info("请求移动云接口查询两张照片是否相似 method:POST image1:{} image2:{}", image1.getName(), image2.getName());
        EcloudBaseResponseVO<FaceMatcherResponseVO> responseVO = commonService.submitMessagePost(faceMatchVO, matcherUrl, new TypeToken<EcloudBaseResponseVO<FaceMatcherResponseVO>>(){}.getType());
        log.info("\n返回消息状态为:{}\n检测人脸比对置信度为:{}\n耗时:{}s\n图1标识id:{}  图2标识id:{}\n图1检测信息:{}\n图2检测信息:{}",
                responseVO.getState(),responseVO.getBody().getConfidence(), responseVO.getBody().getCost(), responseVO.getBody().getImageId1(), responseVO.getBody().getImageId2(),
                JSON.toJSONString(responseVO.getBody().getFaces1()),
                JSON.toJSONString(responseVO.getBody().getFaces2()));
        return responseVO;
    }

    /**
     * 人脸检测
     * @param image
     * @return
     * @throws IOException
     */
    public EcloudBaseResponseVO<FaceDetectResponseVO> detector(MultipartFile image) throws IOException {
        FaceDetectVO faceDetectVO = new FaceDetectVO(image);
        String detecterUrl = commonService.compileUrl(HttpURI.FACE_DETECTTION);
        log.info("请求移动云接口进行人脸检测 method:POST image:{}", image.getName());
        EcloudBaseResponseVO<FaceDetectResponseVO> responseVO = commonService.submitMessagePost(faceDetectVO, detecterUrl, new TypeToken<EcloudBaseResponseVO<FaceDetectResponseVO>>(){}.getType());
        log.info("\n返回消息状态为:{} 检测到的人脸数为:{} \n ", responseVO.getState(), responseVO.getBody().getFaceNum());
        if (!CheckOutUtil.ListIsEmpty(responseVO.getBody().getFaceDetectDetailList())) {
            responseVO.getBody().getFaceDetectDetailList().stream().forEach(faceDetectDetail -> {
                //todo:人脸角度、人脸表情等参数现并不可用，待之后迭代
                FaceDetectResponseVO.FaceDetectRectangleArea rectangleArea = faceDetectDetail.getFaceDectectRectangleArea();
                log.info("检测出来的人脸矩形框的信息:\nfaceId:{}\n矩形左上角:({}, {}) 矩形右下角:({},{}) ",
                        faceDetectDetail.getFaceId(),
                        rectangleArea.getUpperLeftX(), rectangleArea.getUpperLeftY(),
                        rectangleArea.getLowerRightX(), rectangleArea.getLowerRightY());
                log.info("人脸检测结果的关键点数量: {}个", faceDetectDetail.getFaceLandmarkNumber());
                if (faceDetectDetail.getFaceLandmarkNumber() > 0) {
                    faceDetectDetail.getFaceDetectLandmarkAreaList().stream().forEach(faceDetectLandmarkArea -> {
                        log.info("faceId:{} 人脸关键点:({},{})", faceDetectDetail.getFaceId(), faceDetectLandmarkArea.getPointX(), faceDetectLandmarkArea.getPointY());
                    });
                }
            });
        }
        return responseVO;
    }

    /**
     * 人脸标记
     * @param image
     * @return
     * @throws IOException
     */
    public EcloudBaseResponseVO<File> detectAndDraw(MultipartFile image) throws IOException {
        FaceDetectResponseVO detectResponseVO = detector(image).getBody();
        EcloudBaseResponseVO<File> response = new EcloudBaseResponseVO<>();
        File tempFile = File.createTempFile(RandomStringUtils.randomNumeric(10), "jpg");
        BufferedImage bufferedImage = ImageIO.read(image.getInputStream());
        Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();
        graphics.setColor(Color.RED);
        graphics.setStroke(new BasicStroke(3.0f));
        if (!CheckOutUtil.ListIsEmpty(detectResponseVO.getFaceDetectDetailList())) {
            for (FaceDetectResponseVO.FaceDetectDetail faceDetectDetail : detectResponseVO.getFaceDetectDetailList()) {
                FaceDetectResponseVO.FaceDetectRectangleArea rectangleArea = faceDetectDetail.getFaceDectectRectangleArea();
                drawRect(graphics, rectangleArea);
            }
            FileOutputStream outputStream = new FileOutputStream(tempFile);
            ImageIO.write(bufferedImage, "jpeg", outputStream);
            graphics.dispose();
        }
        response.setBody(tempFile);
        return response;
    }

    private void drawRect(Graphics2D graphics, FaceDetectResponseVO.FaceDetectRectangleArea rectangleArea) {
        graphics.drawRect(
                Math.round(rectangleArea.getUpperLeftX()),
                Math.round(rectangleArea.getUpperLeftY()),
                Math.round(rectangleArea.getLowerRightX() - rectangleArea.getUpperLeftX()),
                Math.round(rectangleArea.getLowerRightY() - rectangleArea.getUpperLeftY()));
    }

    /**
     * 客流统计
     * @param image
     * @return
     * @throws IOException
     */
    public EcloudBaseResponseVO<FaceDensityDetectResponseVO> customersAccount(MultipartFile image) throws IOException {
        FaceDensityDetectVO densityDetectVO = new FaceDensityDetectVO(image);
        String densityDetectUrl = commonService.compileUrl(HttpURI.FACE_DENSITY_DETECT);
        log.info("请求移动云接口进行客流统计 method:POST image:{}", image.getName());
        EcloudBaseResponseVO<FaceDensityDetectResponseVO> responseVO = commonService.submitMessagePost(densityDetectVO, densityDetectUrl, new TypeToken<EcloudBaseResponseVO<FaceDensityDetectResponseVO>>(){}.getType());
        log.info("\n返回消息状态为:{} 检测到的人脸数为:{} 图片标识为:{}\n ", responseVO.getState(), responseVO.getBody().getFaceNum(), responseVO.getBody().getImageId());
        if (!CheckOutUtil.ListIsEmpty(responseVO.getBody().getFaceDetails())) {
            responseVO.getBody().getFaceDetails().forEach(faceDetails -> {
                log.info("人脸坐标信息: 左上角坐标:({},{}) 矩形宽:{} 高:{} 可信度:{}", faceDetails.getLeftX(), faceDetails.getLeftY(), faceDetails.getWight(), faceDetails.getHight(), faceDetails.getConfidence());
            });
        }
        return responseVO;
    }

    /**
     * 创建人脸集合
     * @param faceSetName
     * @param description
     * @return
     */
    public EcloudBaseResponseVO<FaceSetCreateResponseVO> createFaceSet(String faceSetName, String description) {
        FaceSetCreateVO faceSetCreateVO = new FaceSetCreateVO(faceSetName, description);
        String faceSetCreateUrl = commonService.compileUrl(HttpURI.FACE_SET_CREATE);
        log.info("请求移动云接口进行人脸集合（即人脸库）创建 method:POST 名称为:{}", faceSetName);
        EcloudBaseResponseVO<FaceSetCreateResponseVO> responseVO = commonService.submitMessagePost(faceSetCreateVO, faceSetCreateUrl, new TypeToken<EcloudBaseResponseVO<FaceSetCreateResponseVO>>(){}.getType());
        log.info("\n返回消息状态为:{} 人脸集合id:{} 名称:{}", responseVO.getState(), responseVO.getBody().getFaceStoreId(), responseVO.getBody().getFaceStoreName());
        return responseVO;
    }

    /**
     * 删除已创建的人脸集合
     * @param faceSetIdList
     * @return
     */
    public EcloudBaseResponseVO<FaceSetDeleteResponseVO> deleteFaceSet(List<String> faceSetIdList) {
        FaceSetDeleteVO faceSetDeleteVO = new FaceSetDeleteVO(faceSetIdList);
        String faceDeleteUrl = commonService.compileUrl(HttpURI.FACE_SET_DELETE);
        log.info("请求移动云接口进行人脸集合（即人脸库）删除 method:POST id:{}", faceSetDeleteVO.getFaceSetIds());
        EcloudBaseResponseVO<FaceSetDeleteResponseVO> responseVO = commonService.submitMessagePost(faceSetDeleteVO, faceDeleteUrl, new TypeToken<EcloudBaseResponseVO<FaceSetDeleteResponseVO>>(){}.getType());
        log.info("\n返回消息状态为:{} ", responseVO.getState());
        return responseVO;
    }

    /**
     * 通过页码和页大小
     * 查询已创建的人脸集合
     * @param pageNum
     * @param pageSize
     * @return
     */
    public EcloudBaseResponseVO<FaceSetFindResponseVO> findFaceSet(int pageNum, int pageSize) {
        return findFaceSet(null, pageNum, pageSize);
    }

    /**
     * 通过人脸集合名称、页码和页大小
     * 查询已创建的人脸集合
     * @param name
     * @param pageNum
     * @param pageSize
     * @return
     */
    public EcloudBaseResponseVO<FaceSetFindResponseVO> findFaceSet(String name, int pageNum, int pageSize) {
        FaceSetFindVO faceSetFindVO = new FaceSetFindVO(name, pageNum, pageSize);
        String faceSetFinfUrl = commonService.compileUrl(HttpURI.FACE_SET_FIND);
        log.info("请求移动云接口进行人脸集合（即人脸库）查询 method:GET name:{} pageNum:{} pageSize:{}", name, pageNum, pageSize);
        EcloudBaseResponseVO<FaceSetFindResponseVO> responseVO = commonService.submitMessageGet(faceSetFinfUrl, faceSetFindVO.getFieds(), new TypeToken<EcloudBaseResponseVO<FaceSetFindResponseVO>>(){}.getType());
        log.info("\n返回消息状态为:{} 数量:{}", responseVO.getState(), responseVO.getBody() == null ? "不存在" : responseVO.getBody().getTotal());
        if (responseVO.getBody().getTotal() > 0) {
            responseVO.getBody().getList().stream().forEach(faceSetResult -> {
                LocalDateTime createTime = commonService.unusualTimeformateToLocalDatetime(faceSetResult.getCreateTime());
                log.info("id :{} 描述:{} 图片数量:{} 创建时间:{}", faceSetResult.getId(), faceSetResult.getDescription(), faceSetResult.getCount(), createTime.toString());
            });
        }
        return responseVO;
    }

    /**
     * 向指定人脸集合添加人员信息和图片
     * @param userName
     * @param faceSetId
     * @param image
     * @return
     * @throws IOException
     */
    public EcloudBaseResponseVO<FaceCreateResponseVO> addFaceMessageToFaceSet(String userName, int faceSetId, MultipartFile image) throws IOException {
        FaceCreateVO faceCreateVO = new FaceCreateVO(userName, faceSetId, image);
        String faceCreateUrl = commonService.compileUrl(HttpURI.FACE_SET_USER_ADD);
        log.info("请求移动云接口进行人脸库中人员添加 method:POST userName:{} faceSetId:{} ", userName, faceSetId);
        EcloudBaseResponseVO<FaceCreateResponseVO> responseVO = commonService.submitMessagePost(faceCreateVO, faceCreateUrl, new TypeToken<EcloudBaseResponseVO<FaceCreateResponseVO>>(){}.getType());
        log.info("\n返回消息状态为:{} 人脸的id:{}", responseVO.getState(), responseVO.getBody().getFaceId());
        return responseVO;
    }

    /**
     * 删除指定人脸集合的指定人员照片等信息
     * @param faceId
     * @param faceSetId
     * @return
     */
    public EcloudBaseResponseVO<FaceDeleteResponseVO> deleteFaceMessageFromFaceSet(int faceId, int faceSetId) {
        FaceDeleteVO faceDeleteVO = new FaceDeleteVO(faceSetId, faceId);
        String faceDeleteUrl = commonService.compileUrl(HttpURI.FACE_SET_USER_DELETE);
        EcloudBaseResponseVO<FaceDeleteResponseVO> responseVO = commonService.submitMessagePost(faceDeleteVO, faceDeleteUrl, new TypeToken<EcloudBaseResponseVO<FaceDeleteResponseVO>>(){}.getType());
        log.info("\n返回消息状态为:{} ", responseVO.getState());
        return responseVO;
    }

    /**
     * 查询指定人脸集合的指定人员图片Id和id数量
     * @param faceId
     * @param faceSetId
     * @return
     */
    public EcloudBaseResponseVO<FaceFindResponseVO> findFaceMessageFromFaceSet(int faceId, int faceSetId) {
        FaceFindVO faceFindVO = new FaceFindVO(faceSetId, faceId);
        String faceFindUrl = commonService.compileUrl(HttpURI.FACE_SET_USER_FIND);
        EcloudBaseResponseVO<FaceFindResponseVO> responseVO = commonService.submitMessagePost(faceFindVO, faceFindUrl, new TypeToken<EcloudBaseResponseVO<FaceFindResponseVO>>(){}.getType());
        log.info("\n返回消息状态为:{} userName:{} faceId:{} 上传图片数量:{}", responseVO.getState(), responseVO.getBody().getFaceName(), faceId,
                CheckOutUtil.ListIsEmpty(responseVO.getBody().getFaceInfoImageList()) ? 0 : responseVO.getBody().getFaceInfoImageList().size() );
        return responseVO;
    }

    /**
     * 通过图片查找指定分组中的人员及其人员信息
     * @param faceCloseNumber
     * @param faceSetId
     * @param image
     * @return
     * @throws IOException
     */
    public EcloudBaseResponseVO<FaceSearchResponseVO> searchFaceInFaceSetFromImage(int faceCloseNumber, String faceSetId, MultipartFile image) throws IOException {
        FaceSearchVO faceSearchVO = new FaceSearchVO(faceSetId, faceCloseNumber, image);
        String faceSearchUrl = commonService.compileUrl(HttpURI.FACE_SET_USER_SEARCH);
        EcloudBaseResponseVO<FaceSearchResponseVO> responseVO = commonService.submitMessagePost(faceSearchVO, faceSearchUrl, new TypeToken<EcloudBaseResponseVO<FaceSearchResponseVO>>(){}.getType());
        log.info("\n返回消息状态为:{} ", responseVO.getState());
        return responseVO;
    }

    public EcloudBaseResponseVO<File> drawFaceInFaceSetFromImage(int faceCloseNumber, String faceSetId, MultipartFile image) throws IOException {
        EcloudBaseResponseVO<FaceSearchResponseVO> responseVO = searchFaceInFaceSetFromImage(faceCloseNumber, faceSetId, image);
        File tempFile = File.createTempFile(RandomStringUtils.randomNumeric(10), "jpg");
        BufferedImage bufferedImage = ImageIO.read(image.getInputStream());
        Graphics2D graphics = (Graphics2D) bufferedImage.getGraphics();
        EcloudBaseResponseVO<File> response = new EcloudBaseResponseVO<>();
        graphics.setColor(Color.RED);
        graphics.setStroke(new BasicStroke(3.0f));
        if (!CheckOutUtil.ListIsEmpty(responseVO.getBody().getFaces())) {
            StringBuilder personInfoBuilder = new StringBuilder();
            for (FaceDetectResponseVO.FaceDetectRectangleArea rectangleArea : responseVO.getBody().getFaces()) {
                drawRect(graphics, rectangleArea);
            }
            for (FaceSearchResponseVO.FaceSearchInfoResult faceSearchInfoResult : responseVO.getBody().getResults()) {
                personInfoBuilder.append("名称: " + faceSearchInfoResult.getFaceName() + ", 可信度: " + faceSearchInfoResult.getConfidence()+"\n");
            }
            graphics.drawString(personInfoBuilder.toString(), 0, 0);
            FileOutputStream outputStream = new FileOutputStream(tempFile);
            ImageIO.write(bufferedImage, "jpeg", outputStream);
            graphics.dispose();
        }
        response.setBody(tempFile);
        return response;
    }
}